package org.linloong.config.monitor;

import io.swagger.v3.oas.annotations.Hidden;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;

@Component
@RequiredArgsConstructor
@Hidden // Swagger隐藏内部服务类，不对外暴露
@Slf4j
public class TokenManager {
    // 文档中Token请求固定参数（"数据令牌请求"章节Request body）
    private static final String CLIENT_ID = "webClient";
    private static final String GRANT_TYPE = "password";
    private static final String CLIENT_SECRET = "MainsiteSecret";
    private static final String TOKEN_URI = "/connect/token";

    @Autowired
    private MonitorConfig monitorConfig;

    private final WebClient.Builder webClientBuilder;
    private TokenVO currentToken; // 缓存有效Token

    /**
     * 获取有效Token（过期自动刷新，对应文档"查询流程"第1步）
     */
    public String getValidToken() {
        if (currentToken == null || isTokenExpired(currentToken)) {
            try {
                currentToken = refreshToken().block(); // 同步获取Token
            } catch (Exception e) {
                log.error("获取Token失败: ", e);
                // 发生异常时，清空当前token，下次重试
                currentToken = null;
                throw e;
            }
        }
        return currentToken.getAccess_token();
    }

    /**
     * 刷新Token（文档"数据令牌请求"章节：POST /connect/token）
     */
    private Mono<TokenVO> refreshToken() {
        String baseUrl = monitorConfig.getBaseUrl();
        String username = monitorConfig.getUsername();
        String password = monitorConfig.getPassword();
        
        log.info("正在获取Token: baseUrl={}, username={}", baseUrl, username);
        
        return webClientBuilder.build()
                .post()
                .uri(baseUrl + TOKEN_URI)
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .body(BodyInserters.fromFormData("client_id", CLIENT_ID)
                        .with("grant_type", GRANT_TYPE)
                        .with("username", username)
                        .with("password", password)
                        .with("client_secret", CLIENT_SECRET))
                .retrieve()
                .onStatus(status -> status.is4xxClientError() || status.is5xxServerError(),
                        response -> {
                            log.error("获取Token失败，状态码: {}, 响应: {}", response.statusCode(), response);
                            return Mono.error(new RuntimeException("获取Token失败: " + response.statusCode()));
                        })
                .bodyToMono(TokenVO.class)
                .doOnNext(token -> {
                    token.setCreateTime(System.currentTimeMillis());
                    log.info("Token获取成功，有效期: {}秒", token.getExpires_in());
                })
                .onErrorMap(WebClientResponseException.class, ex -> {
                    log.error("获取Token时发生WebClient异常: 状态码={}, 响应体={}", ex.getStatusCode(), ex.getResponseBodyAsString());
                    return ex;
                })
                .onErrorMap(Exception.class, ex -> {
                    log.error("获取Token时发生异常: ", ex);
                    return ex;
                });
    }

    /**
     * 判断Token是否过期（提前60s刷新，避免临界过期）
     */
    private boolean isTokenExpired(TokenVO token) {
        if (token == null || token.getCreateTime() == null || token.getExpires_in() == null) {
            return true;
        }
        long expireTime = token.getCreateTime() + (token.getExpires_in() * 1000) - 60000;
        return System.currentTimeMillis() > expireTime;
    }
}